var event = require('../../core/event');
var util = require('../../util/index');
var Log = require('../../util/logger');
var note = require('../note/manager');
var frac = require('../../util/frac');
var sample = require('../sample/manager');
var sound = require('../audio/sound');

var _inited = false;
var _default = 120;

/**
作为grid的辅助类存在
 */

var ret = {
	init: function(){
		if(_inited){
			return;
		}
		this.timeTable = null;
		this.chart = null;
		event.bind(event.events.dialog.bpm,'timing', this);
		_inited = true;
	},

	onEvent: function(e, param) {
		if(e == event.events.dialog.bpm){
			if(!this.chart){
				return;
			}
			var dialog = require('./dialog');
			dialog.setManager(this);
			dialog.init();
			dialog.show(param);
		}
	},

	load: function(chart){
		this.chart = chart;
		this.loadTimeTable();
	},

	unload: function(){
		this.chart = null;
		this.timeTable = null;
	},

	getAll: function() {
		return this.timeTable;
	},

	/**
	 * 操纵接口
	 */

	setStartBPM: function(bpm) {
		if(!this.chart){
			return false;
		}
		var tp = this.getPointAt([0, 0, 0]);
		if(tp){
			if(tp.bpm == bpm){
				return false;
			}
			tp.bpm = bpm;
		}else{
			this.timeTable.unshift({
				beat:[0,0,1],
				bpm: bpm,
				signature: 4
			});
		}
		this.updateTimeTable();
		event.trigger(event.events.note.change);
		return true;
	},

	getStartBPM: function(){
		if(!this.chart){
			return 120;
		}
		var tp = this.getPointAt([0, 0, 0]);
		if(tp){
			return tp.bpm;
		}
		return 120;
	},

	/**
	 * 增加一个非基准点
	 */
	add: function(tp){
		if(!this.chart){
			return false;
		}
		var old = this.getPointAt(tp.beat);
		if(old){
			old.bpm = tp.bpm;
			old.multiplier = tp.multiplier;
		}else{
			this.timeTable.push(tp);
			this.sortTime();
		}
		this.updateTimeTable();
		event.trigger(event.events.note.change);
		return true;
	},

	remove: function(tp){
		if(!this.chart){
			return false;
		}
		//不能删第 1个tp, 这是基准
		if(tp.beat[0] == 0 && tp.beat[1] == 0){
			return false;
		}
		var idx = util.binaryFindIndex(this.timeTable, function(item){
			return frac.compare(tp.beat, item.beat);
		});
		if(idx < 0){
			return false;
		}
		if(frac.compare(this.timeTable[idx].beat, tp.beat) != 0){
			return false;
		}
		this.timeTable.splice(idx, 1);
		this.updateTimeTable();
		event.trigger(event.events.note.change);
		return true;
	},

	/**
	 * 修改一个存在点的值
	 * 特别主要, 0点的值, beat不能变, bpm变化要同步到grid
	 * @param _old
	 * @param _new
	 * @returns {boolean}
	 */
	change: function(_old, _new) {
		if(!this.chart){
			return false;
		}
		var old = this.getPointAt(_old.beat);
		if(!old){
			return false;
		}
		old.multiplier = _new.multiplier;
		if(typeof _new.bpm != 'undefined'){
			old.bpm = _new.bpm;
		}
		//if(_old.beat[0] == 0 && _old.beat[1] == 0){
		//	return true;
		//}
		//只有beat发生变化时, 才需要重排序, 重算
		if(_new.beat && frac.compare(_old.beat, _new.beat) != 0){
			old.beat = _new.beat;
			this.sortTime();
		}
		this.updateTimeTable();
		event.trigger(event.events.note.change);
		return true;
	},

	// 从谱面中载入timing
	loadTimeTable: function() {
		if (!this.chart) {
			return;
		}
		this.timeTable = this.chart.time;
		this.sortTime();
		if(this.timeTable.length == 0){
			//确保timeTable至少有1个点
			this.timeTable.push({
				beat: [0, 0, 1],
				bpm: _default,
				signature: 4,
				measure:[0,0,4],
				time: 0
			});
		}
		this.updateTimeTable();
		event.trigger(event.events.note.change);
	},

	// 更新beat和时间的对应关系，要求timeTable有序
	updateTimeTable: function() {
		var prevTimePoint = this.timeTable[0];
		prevTimePoint.time = 0;
		//prevTimePoint.measure = [0,0,4];
		for (var i = 1; i < this.timeTable.length; i++) {
			var timePoint = this.timeTable[i];
			//存在相同beat的时候, 只有第一个有效
			//相同beat理论上只有手动编辑才存在, 在正式输出会干掉, mce中保留, 且可编辑
			if(frac.compare(prevTimePoint.beat, timePoint.beat) == 0){
				continue;
			}
			timePoint.time = prevTimePoint.time + (60000 / prevTimePoint.bpm) * frac.toFloat(frac.subtract(timePoint.beat, prevTimePoint.beat));
		/*	timePoint.measure = frac.add(prevTimePoint.measure, [0, timePoint.beat[0] - prevTimePoint.beat[0], prevTimePoint.signature]);
			if (timePoint.signature) {
				if (timePoint.measure[1] == 0) {
					timePoint.measure = [timePoint.measure[0], 0, timePoint.signature];
				} else {
					timePoint.measure = [timePoint.measure[0] + 1, 0, timePoint.signature];
				}
			} else {
				timePoint.signature = prevTimePoint.signature;
			}*/
			prevTimePoint = timePoint;
		}
	},

	sortTime: function(){
		if(!this.timeTable){
			return;
		}
		this.timeTable.sort(function(a, b) { return frac.compare(a.beat, b.beat); });
	},

	getPointAt: function(beat) {
		var tp = this.timeTable[util.binaryFindIndex(this.timeTable, function(item){
			return frac.compare(beat, item.beat);
		})];
		if(!tp){
			return null;
		}
		if(frac.compare(beat, tp.beat) == 0){
			return tp;
		}
		return null;
	},

	/**
	 * 节拍和时间的换算
	 */

	beatToTime: function(beat) {
		if(!this.timeTable){
			Log.e("invalide call to beatToTime", "Time");
			return 0;
		}
		var timePoint = this.timeTable[util.binaryFindIndex(this.timeTable, function(item) {
			return frac.compare(beat, item.beat);
		})];
		//beat小于最小tp时 搜索结果是null
		if(!timePoint){
			timePoint = this.timeTable[0];
		}
		return timePoint.time + (60000 / timePoint.bpm) * frac.toFloat(frac.subtract(beat, timePoint.beat));
	},

	timeToBeat: function(time, den) {
		if(!this.timeTable){
			Log.e("invalide call to timeToBeat", "Time");
			return null;
		}
		var tp = this.timeTable[util.binaryFindIndex(this.timeTable, function(item) {
			return time - item.time;
		})];
		if(!den){
			den = 4;
		}
		var beat = frac.add(tp.beat, frac.fromFloat((time - tp.time) / (60000 / tp.bpm), den));
		return beat;
	},

	floatBeatToTime: function(floatBeat) {
		if(!this.timeTable){
			Log.e("invalide call to floatBeatToTime", "Time");
			return 0;
		}
		var timePoint = this.timeTable[util.binaryFindIndex(this.timeTable, function(item) {
			return floatBeat - frac.toFloat(item.beat);
		})];
		return timePoint.time +  (floatBeat - frac.toFloat(timePoint.beat) * (60000 / timePoint.bpm));
	},

	timeToFloatBeat: function(time) {
		if(!this.timeTable){
			Log.e("invalide call to timeToFloatBeat", "Time");
			return 0;
		}
		var timePoint = this.timeTable[util.binaryFindIndex(this.timeTable, function(item) {
			return time - item.time;
		})];
		return frac.toFloat(timePoint.beat) + (time - timePoint.time) / (60000 / timePoint.bpm);
	},

	beatToFloat : function(beat){
		return beat[0] + beat[1] / beat[2];
	}
};

module.exports = ret;